package com.easipass.framework.eirframeworkcoredsl;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class AppValidatorVisitor extends Visitor {

    private String appDsl;

    private List<AppValidationProblem> problems = new ArrayList<>();

    private List<LabelledServiceNode> recordedSequences = new ArrayList<>();

    private Set<TransitionNode> transitionsTargetingLabels = new HashSet<>();

    private Set<String> labelsDefined = new HashSet<>();

    private Set<String> appNamesWithoutLabels = new HashSet<>();

    public List<AppValidationProblem> getProblems() {
        return problems;
    }

    public boolean hasProblems() {
        return problems.size() != 0;
    }

    public void reset() {
        this.problems.clear();
        this.recordedSequences.clear();
        this.transitionsTargetingLabels.clear();
        this.labelsDefined.clear();
        this.appNamesWithoutLabels.clear();
    }

    @Override
    public void startVisit(String appName, String appDsl) {
        this.appDsl = appDsl;
    }

    public boolean preVisitSequence(LabelledServiceNode firstNode) {
        if (super.sequenceNumber > 0 && !firstNode.hasLabel()) {
            pushProblem(firstNode.getStartPos(), DSLMessage.SERVICE_VALIDATION_SECONDARY_SEQUENCES_MUST_BE_NAMED);
        }
        recordedSequences.add(firstNode);
        return true;
    }

    @Override
    public boolean preVisit(SplitNode split) {
        if (split.hasLabel()) {
            String labelString = split.getLabelString();
            if (labelsDefined.contains(labelString)) {
                pushProblem(split.getLabel().startPos, DSLMessage.SERVICE_VALIDATION_DUPLICATE_LABEL);
            }
            labelsDefined.add(labelString);
        }
        return true;
    }

    @Override
    public void visit(ServiceNode serviceApp) {
        if (serviceApp.hasLabel()) {
            String labelString = serviceApp.getLabelString();
            if (labelsDefined.contains(labelString)) {
                pushProblem(serviceApp.getLabel().startPos, DSLMessage.SERVICE_VALIDATION_DUPLICATE_LABEL);
            }
            labelsDefined.add(labelString);
            if (appNamesWithoutLabels.contains(labelString)) {
                pushProblem(serviceApp.getLabel().startPos, DSLMessage.SERVICE_VALIDATION_LABEL_CLASHES_WITH_APP_NAME);
            }
        } else {
            String name = serviceApp.getName();
            if (labelsDefined.contains(name)) {
                pushProblem(serviceApp.startPos, DSLMessage.SERVICE_VALIDATION_APP_NAME_CLASHES_WITH_LABEL);
            }
            if (appNamesWithoutLabels.contains(name)) {
                pushProblem(serviceApp.startPos, DSLMessage.SERVICE_VALIDATION_APP_NAME_ALREADY_IN_USE);
            }
            appNamesWithoutLabels.add(serviceApp.getName());
        }
    }

    @Override
    public void visit(TransitionNode transition) {
        if (!transition.isTargetService()) {
            transitionsTargetingLabels.add(transition);
        }
        if (transition.isTargetService()) {
            ServiceNode serviceApp = transition.getTargetService();
            if (serviceApp.hasLabel()) {
                String labelString = serviceApp.getLabelString();
                if (labelsDefined.contains(labelString)) {
                    pushProblem(serviceApp.getLabel().startPos, DSLMessage.SERVICE_VALIDATION_DUPLICATE_LABEL);
                }
                labelsDefined.add(labelString);
                if (appNamesWithoutLabels.contains(labelString)) {
                    pushProblem(serviceApp.getLabel().startPos,
                            DSLMessage.SERVICE_VALIDATION_LABEL_CLASHES_WITH_APP_NAME);
                }
            } else {
                String name = serviceApp.getName();
                if (labelsDefined.contains(name)) {
                    pushProblem(serviceApp.startPos, DSLMessage.SERVICE_VALIDATION_APP_NAME_CLASHES_WITH_LABEL);
                }
                if (appNamesWithoutLabels.contains(name)) {
                    pushProblem(serviceApp.startPos, DSLMessage.SERVICE_VALIDATION_APP_NAME_ALREADY_IN_USE);
                }
                appNamesWithoutLabels.add(serviceApp.getName());
            }
        }
    }

    @Override
    public void endVisit() {
        for (TransitionNode transitionTargetingLabel : transitionsTargetingLabels) {
            if (!labelsDefined.contains(transitionTargetingLabel.getTargetLabel())) {
                pushProblem(transitionTargetingLabel.startPos,
                        DSLMessage.SERVICE_VALIDATION_TRANSITION_TARGET_LABEL_UNDEFINED);
            }
        }
    }

    private void pushProblem(int pos, DSLMessage message) {
        problems.add(new AppValidationProblem(appDsl, pos, message));
    }
}
